package com.sunpy.permissionservice.login.filter;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sunpy.commonservice.model.ResultModel;
import com.sunpy.commonservice.util.HttpUtil;
import com.sunpy.commonservice.util.JwtUtil;
import com.sunpy.commonservice.util.RedisCache;
import com.sunpy.permissionservice.dao.BlackWhiteMapper;
import com.sunpy.permissionservice.login.model.LoginUserDetailBO;
import com.sunpy.permissionservice.login.service.IUserService;
import com.sunpy.permissionservice.po.BlackWhite;
import com.sunpy.permissionservice.service.IBlackWhiteService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Objects;

/**
 * <p>
 *  用户认证登录、黑白名单
 * </p>
 *
 * @author sunpy
 * @since 2022-09-260
 */
@Slf4j
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private RedisCache redisCache;

    @Value("${sys.login-uri}")
    private String loginUri;

    @Autowired
    private IBlackWhiteService blackWhiteService;

    @SneakyThrows
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) {
        log.info("+++++++++++++++++++JwtAuthenticationTokenFilter.doFilterInternal");
        String ip = request.getHeader("x-forwarded-for");
        String username = request.getParameter("username");
        String jwt = request.getHeader("token");

        /**
         * 限制请求的IP
         */
        if (StrUtil.isNotBlank(ip) && !checkRequestByIP(ip)) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            HttpUtil.setRespBody(response, HttpUtil.getErrorResult(HttpServletResponse.SC_FORBIDDEN,
                    "此IP已加入黑名单，禁止访问", null));
            return;
        }

        if (StrUtil.isNotBlank(username) && !checkRequestByUsername(username)) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            HttpUtil.setRespBody(response, HttpUtil.getErrorResult(HttpServletResponse.SC_FORBIDDEN,
                    "用户已加入黑名单，禁止访问", null));
            return;
        }

        if (StrUtil.isNotBlank(jwt) && !checkRequestByJWT(jwt)) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            HttpUtil.setRespBody(response, HttpUtil.getErrorResult(HttpServletResponse.SC_FORBIDDEN,
                    "用户已加入黑名单，禁止访问", null));
            return;
        }

        /**
         * jwt空的情况
         */
        if (StrUtil.isBlank(jwt)) {
            // 当前登录的url为/user/login
            if (loginUri.equals(request.getRequestURI())) {
                // 业务请求向后过滤
                filterChain.doFilter(request, response);
            } else {
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                HttpUtil.setRespBody(response, HttpUtil.getErrorResult(HttpServletResponse.SC_UNAUTHORIZED, "没有权限", null));
            }
            return;
        }

        /**
         * token非空，检查是否有效
         */
        if (!JwtUtil.checkJWT(jwt, "userId")) {
            response.setStatus(HttpServletResponse.SC_PAYMENT_REQUIRED);
            HttpUtil.setRespBody(response, HttpUtil.getErrorResult(HttpServletResponse.SC_PAYMENT_REQUIRED,
                    "token已失效，请重新登陆", null));
            return;
        }

        String userId = (String) JwtUtil.parseJWT(jwt).get("userId");

        /**
         * 判断当前的用户是否已拉入黑名单
         */
        if (!checkRequestByUserId(userId)) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            HttpUtil.setRespBody(response, HttpUtil.getErrorResult(HttpServletResponse.SC_FORBIDDEN,
                    "用户已加入黑名单，禁止访问", null));
            return;
        }

        LoginUserDetailBO loginUserDetailBO = redisCache.getCacheObject("token:" + userId);

        if (Objects.isNull(loginUserDetailBO)) {
            response.setStatus(HttpServletResponse.SC_PAYMENT_REQUIRED);
            HttpUtil.setRespBody(response, HttpUtil.getErrorResult(HttpServletResponse.SC_PAYMENT_REQUIRED,
                    "token已失效，请重新登陆", null));
            return;
        }

        /**
         * 将登录用户及其权限放入上下文中
         */
        SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(
                loginUserDetailBO, null, loginUserDetailBO.getAuthorities()
        ));

        if (loginUri.equals(request.getRequestURI())) {
            response.setStatus(HttpServletResponse.SC_OK);
            HttpUtil.setRespBody(response, HttpUtil.getSuccessResult(HttpServletResponse.SC_OK,
                    "登录成功", null));
            return;
        }

        filterChain.doFilter(request, response);
    }

    /**
     * 黑白名单，禁止userId
     * @param userId
     */
    private boolean checkRequestByUserId(String userId) {
        BlackWhite blackWhite = new BlackWhite();
        blackWhite.setUserId(Long.valueOf(userId));
        ResultModel<List<BlackWhite>> resultModel = blackWhiteService.queryByWrapper(blackWhite);

        return CollectionUtil.isEmpty(resultModel.getRes());
    }

    /**
     * 黑白名单，禁止username
     * @param username
     * @return
     */
    private boolean checkRequestByUsername(String username) {
        List<BlackWhite> list = blackWhiteService.selectUserIdByUsername(username);
        return CollectionUtil.isEmpty(list);
    }

    /**
     * 黑白名单 ，禁止ip地址
     * @param ip
     */
    private boolean checkRequestByIP(String ip) {
        BlackWhite blackWhite = new BlackWhite();
        blackWhite.setUserIp(ip);
        ResultModel<List<BlackWhite>> resultModel = blackWhiteService.queryByWrapper(blackWhite);
        return CollectionUtil.isEmpty(resultModel.getRes());
    }

    /**
     * 黑白名单，禁止jwt
     * @param jwt
     * @return
     */
    private boolean checkRequestByJWT(String jwt) {
        BlackWhite blackWhite = new BlackWhite();
        blackWhite.setJwtCode(jwt);
        ResultModel<List<BlackWhite>> resultModel = blackWhiteService.queryByWrapper(blackWhite);
        return CollectionUtil.isEmpty(resultModel.getRes());
    }
}

